/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package aggr

import scala.collection.JavaConversions._
import scala.collection.mutable.{Set => MSet}
import com.google.protobuf.Descriptors.Descriptor
import com.google.protobuf.Descriptors.FieldDescriptor
import com.google.protobuf.Descriptors.FieldDescriptor.Type
import SerializedTypes._

/**
 * Module for utilities.
 */
object Util {

  /**
   * SerializedTypes is number?
   * If t is basic number type or Message type which has number type menber only, true.
   * Otherwise false.
   */
  def isNumber(t: SerializedType): Boolean = {
    t match { 
      case BoolType       => false
      case FloatType      => true
      case DoubleType     => true
      case Fixed32Type    => true
      case Fixed64Type    => true
      case SFixed32Type   => true
      case SFixed64Type   => true
      case Int32Type      => true
      case Int64Type      => true
      case SInt32Type     => true
      case SInt64Type     => true
      case UInt32Type     => true
      case UInt64Type     => true
      case StringType     => false
      case BytesType      => false
      case EnumType(_)    => false
      case MessageType(d) => messageIsNumber(d)
    }
  }

  private def messageIsNumber(desc: Descriptor): Boolean = {
    ! desc.getFields().exists { fld => 
      ! ( fld.isRequired() && isNumber(getType(fld)) )
    }
  }

  /**
   * Checks number of arguments.
   * This function is successful when n == expect, then returns Nil.
   * Otherwise returns error message list.
   */
  def checkNumOfArgs(aggr: String, n: Int, expect: Int): List[String] = {
    def expectNArgs = expect match {
      case 0 => "no argument"
      case 1 => "a argument"
      case x => "%s arguments".format(x)
    }

    n match {
      case n if (n < expect) => List("Too few arguments. \"%s\" aggregator expects %s".format(aggr, expectNArgs))
      case n if (n > expect) => List("Too many arguments. \"%s\" aggregator expects %s".format(aggr, expectNArgs))
      case n                 => Nil
    }
  }

  /**
   * Checks aggregator option.
   */
  def checkOptionName(aggr: String, options: Map[String, SerializedType], expect: List[String]): List[String] = {
    val expectSet = Set() ++ expect
    val appear = MSet[String]()

    val l = options.flatMap { case (k, _) => 
      (if (appear.contains(k)) List("\"%s\" option has already been specified.".format(k)) else Nil) ++
      (if (expectSet.contains(k)) { appear += k; Nil} else  List("\"%s\" is unkown option.".format(k)))
    }

    List() ++ l ++ (expectSet -- appear).map { x =>
      "\"%s\" option is required, but appears.".format(x)
    }
  }
}
